package pl.mess.core.tags.writer;

import pl.mess.core.tags.elements.MESSBaseTag;
import pl.mess.core.tags.ifc.MessTagWriterIfc;
import pl.mess.core.tags.ifc.ControlAttribute;

import javax.servlet.jsp.JspWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.io.IOException;

/**
 * Created by IntelliJ IDEA.
 * User: gibffe
 * Date: Mar 12, 2006
 * Time: 1:06:15 PM
 * To change this template use File | Settings | File Templates.
 */
public class MESSXmlWriter implements MessTagWriterIfc {

    private static final String XML_TAG_PREFIX = "<";
    private static final String XML_TAG_END_PREFIX = "</";
    private static final String XML_TAG_SUFFIX = ">";
    
    private JspWriter writer = null;


    /**
     * Default constructor
     * @param writer - jsp writer instance to use
     */
    public MESSXmlWriter(JspWriter writer) {

        this.writer = writer;
    }


    /**
     * Write tag name and all non-null attributes:  <tag attr1='xxx' attr2='xxx' ...>
     * @param tag
     * @return
     */
    public int writeTagStart(MESSBaseTag tag) {

        StringBuilder buffer = new StringBuilder();

        // render tag name
        buffer.append(XML_TAG_PREFIX);
        buffer.append(tag.getTagName());

        // render all non-null tag attributes
        Map attributes = new HashMap();
        for (Field f : tag.getClass().getFields()) {
            if (f.isAnnotationPresent(ControlAttribute.class)) {
                String getterName = this.getGetterName(f);
                // collect only set attributes (ones with value)
                try {
                    Method m = tag.getClass().getMethod(getterName, new Class[]{});
                    Object value = m.invoke(tag, new Object[]{});
                    if (value != null) {
                        attributes.put(f.getName(), value);
                    }
                }
                catch (Exception ex) {
                    // log error
                    ex.printStackTrace();
                }
            }
        }

        // render tag attributes
        for (Iterator it = attributes.keySet().iterator(); it.hasNext();) {

            String key = (String) it.next();
            StringBuilder attrValue = new StringBuilder();
            attrValue.append(" " + key + "=\"");
            attrValue.append(this.getKeyValue(attributes.get(key)));
            attrValue.append("\" ");

            // append to result
            buffer.append(attrValue.toString());
        }

        buffer.append(XML_TAG_SUFFIX);

        // okay, flush buffer to jsp content
        try {
            this.writer.write(buffer.toString());
        }
        catch (IOException ex) {
            // log error
        }


        return 0;
    }

    /**
     * Write closingTag string  </tag>
     * @param tag
     * @return
     */
    public int writeTagEnd(MESSBaseTag tag) {

        StringBuilder buffer = new StringBuilder();

        buffer.append(XML_TAG_END_PREFIX);
        buffer.append(tag.getTagName());
        buffer.append(XML_TAG_SUFFIX);

        // flush buffer to jsp
        try {
            this.writer.write(buffer.toString());
        }
        catch (IOException ex) {
            // log error
        }
        
        return 0;
    }

    /**
     * Return getter name using field name
     * @param f  Field object
     * @return   String representing getter method name
     */
    private String getGetterName(Field f) {

        StringBuilder name = new StringBuilder();
        char[] chars = f.getName().toCharArray();
        chars[0] = Character.toUpperCase(chars[0]);

        name.append("get");
        return name.append(chars).toString();
    }

    /**
     * Get key value logic.
     * @param value
     * @return
     */
    private String getKeyValue(Object value) {

        if (value instanceof String) {
            String _value = (String) value;
            if (!_value.startsWith("'") && !_value.endsWith("'")) {
                _value = "'" + _value + "'";
            }

            return _value;
        }
        else {
            return value.toString();
        }
    }
}
